/**
 * www.easyplatform.cn ©2016
 */
package cn.easyplatform.studio;

import cn.easyplatform.lang.Nums;
import cn.easyplatform.studio.cfg.EngineConfiguration;
import cn.easyplatform.studio.interceptor.Command;
import cn.easyplatform.studio.vos.FunctionVo;
import cn.easyplatform.studio.web.editors.support.NodeFactory;
import cn.easyplatform.utils.resource.Scans;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Elements;
import org.apache.log4j.PropertyConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WebApp;
import org.zkoss.zk.ui.WrongValueException;

import javax.servlet.ServletContext;
import java.io.File;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author <a href="mailto:shiny_vc@163.com">陈云亮</a> <br/>
 * @since 2.0.0 <br/>
 */
public class StudioApp {


    private final static Logger log = LoggerFactory.getLogger(StudioApp.class);

    // 系统启动时间
    private Date startTime;
    // 引擎配置接口
    private EngineConfiguration engineConfiguration;
    //
    private WebApp webapp;

    private Map<String, FunctionVo> functions;

    private static StudioApp me = new StudioApp();

    public final static StudioApp me() {
        return me;
    }

    /**
     * @param webapp
     * @throws Exception
     */
    public void start(WebApp webapp) throws Exception {
        StringBuilder sb = new StringBuilder(webapp.getRealPath(""));
        sb.append(File.separatorChar).append("WEB-INF");
        sb.append(File.separatorChar).append("config")
                .append(File.separatorChar).append("log4j.properties");
        PropertyConfigurator.configure(sb.toString());
        sb.setLength(0);
        sb.append(webapp.getRealPath("")).append(File.separatorChar)
                .append("WEB-INF");
        sb.append(File.separatorChar).append("config")
                .append(File.separatorChar).append("functions.xml");
        functions = new HashMap<String, FunctionVo>();
        File file = new File(sb.toString());
        if (file.exists()) {
            Document doc = new Builder(false, new NodeFactory()).build(file);
            Elements elements = doc.getRootElement().getChildElements();
            Element cnameElms = null;
            Element expElms = null;
            for (int i = 0; i < elements.size(); i++) {
                Element funcElm = elements.get(i);
                String prefix = funcElm.getAttributeValue("prefix");
                prefix = prefix == null ? "" : prefix + ".";
                FunctionVo func = new FunctionVo(prefix
                        + funcElm.getAttributeValue("name"));
                cnameElms = funcElm.getFirstChildElement("cname");
                if(cnameElms != null) {
                    func.setcName(cnameElms.getValue());
                } else {
                    func.setcName("");
                }
                Elements argsElm = funcElm.getChildElements("args");
                for (int j = 0; j < argsElm.size(); j++) {
                    Element argElm = argsElm.get(j);
                    func.addArg(argElm.getAttributeValue("name"), argElm
                            .getAttributeValue("desc").trim());
                    if(j == 0) {
                        func.setExp(argElm.getAttributeValue("name"));
                    }
                }
                Elements examplesElm = funcElm.getChildElements("example");
                for (int j = 0; j < examplesElm.size(); j++) {
                    Element exampleElm = examplesElm.get(j);
                    func.addExample(exampleElm.getValue());
                }
                functions.put(func.getName(), func);
            }
            doc.detach();
            doc = null;
        }
        sb = null;
        if (log.isInfoEnabled())
            log.info(Labels.getLabel("app.starting"));
        Scans.me().init(webapp.getServletContext().getRealPath("/WEB-INF"));
        startTime = new Date();
        this.webapp = webapp;
        this.engineConfiguration = new EngineConfiguration();
        this.engineConfiguration.init(webapp);
        if (log.isInfoEnabled())
            log.info(Labels.getLabel(
                    "app.started",
                    new Object[]{
                            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                                    .format(startTime),
                            System.currentTimeMillis() - startTime.getTime()}));
    }

    /**
     *
     */
    public void stop() {
        if (log.isInfoEnabled())
            log.info(Labels.getLabel("app.stopping"));
        engineConfiguration.close();
        if (log.isInfoEnabled()) {
            long serviceTimes = System.currentTimeMillis()
                    - startTime.getTime();
            serviceTimes = serviceTimes / (1000 * 60 * 60);
            log.info(Labels.getLabel(
                    "app.stopped",
                    new Object[]{
                            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                                    .format(new Date()), serviceTimes}));
        }
        startTime = null;
    }

    // 系统配置
    public int getConfigAsInt(String name) {
        return Nums.toInt(engineConfiguration.getConfig(name), 0);
    }

    public boolean getConfigAsBoolean(String name) {
        return engineConfiguration.getConfig(name) == null ? false
                : engineConfiguration.getConfig(name).equalsIgnoreCase("true");
    }

    public String getConfig(String name) {
        return engineConfiguration.getConfig(name);
    }


    public final static String getHelpConfig(String name) {
        if (name.equals("help.baseUrl.forum") || name.equals("help.baseUrl.guide") || name.equals("help.baseUrl.reference")
                || name.equals("help.baseUrl.component")
                || name.equals("help.baseUrl.logic")
                || name.equals("help.baseUrl")
                || name.equals("help.baseUrl.mall")
                || name.equals("help.baseUrl.module")
                || name.equals("project.task.url")) {
            return me.getConfig(name);
        }else {
            throw new RuntimeException("Access to configuration information is not allowed");
        }
    }

    public final static String getAppPath() {
        return me.getConfig("app.webContainer.path");
    }

    public final static String getAppTemplatePath() {
        return me.getConfig("app.template.path");
    }

    public final static String getApiTestBaseUrl() {
        return me.getConfig("api.test.baseUrl");
    }

    /**
     * 是否显示源码
     *
     * @return
     */
    public final static boolean isShowCode() {
        return me.getConfigAsBoolean("app.showCode");
    }

    /**
     * 是否云部署
     *
     * @return
     */
    public final static boolean isCloudDeployment() {
        return me.getConfigAsBoolean("app.cloudDeployment");
    }

    /**
     * @return
     */
    public final static String getAppName() {
        return me.webapp.getAppName();
    }

    /**
     * @param command
     * @return
     */
    public final static <T> T execute(Command<T> command) {
        return execute(null, command);
    }

    /**
     * @param command
     * @return
     */
    public final static <T> T execute(Component target, Command<T> command) {
        try {
            return me.engineConfiguration.execute(command);
        } catch (Exception ex) {
            if (log.isErrorEnabled())
                log.error("Excecute Command:%s", command.getClass()
                        .getCanonicalName(), ex);
            if (target != null) {
                if (ex instanceof StudioException) {
                    throw new WrongValueException(target, ex.getMessage());
                } else {
                    throw new WrongValueException(target,
                            Labels.getLabel("app.exception"));
                }
            }
            if (ex instanceof StudioException)
                throw (StudioException) ex;
            else {
                throw new StudioException(
                        command.getClass().getCanonicalName(),
                        ex.getCause() == null ? ex : ex.getCause());
            }
        }
    }

    /**
     * @return
     */
    public final static ServletContext getServletContext() {
        return me.webapp.getServletContext();
    }

    /**
     * @param path
     * @return
     */
    public final static InputStream getResourceAsStream(String path) {
        return me.webapp.getResourceAsStream(path);
    }

    /**
     * 获取函数描述信息
     *
     * @param name
     * @return
     */
    public final static FunctionVo getFunctionDesc(String name) {
        return me.functions.get(name);
    }

    public final static Set<String> getAllFunctionName() {
        return me.functions.keySet();
    }

    public final static Map<String, FunctionVo> getAllFunction() {
        return me.functions;
    }
}
